package com.sanhe.roller.util;

import java.nio.ByteBuffer;

public class Crc16Utils {

    /**
     * 获取源数据和验证码的组合byte数组
     *
     * @return
     */
    public static byte[] getData(String string) {
        byte[] data = new byte[]{};
        String[] strings = string.split(" ");
        for (int i = 0; i < strings.length; i++) {
            int x = Integer.parseInt(strings[i], 16);
            byte n = (byte) x;
            byte[] buffer = new byte[data.length + 1];
            byte[] aa = {n};
            System.arraycopy(data, 0, buffer, 0, data.length);
            System.arraycopy(aa, 0, buffer, data.length, aa.length);
            data = buffer;
        }
        return getData(data);
    }

    /**
     * 获取源数据和验证码的组合byte数组
     *
     * @param aa 字节数组
     * @return
     */
    public static byte[] getData(byte[] aa) {
        byte[] bb = getCrc16(aa);
        byte[] cc = new byte[aa.length + bb.length];
        System.arraycopy(aa, 0, cc, 0, aa.length);
        System.arraycopy(bb, 0, cc, aa.length, bb.length);
        return cc;
    }

    /**
     * 获取验证码byte数组，基于Modbus CRC16的校验算法
     */
    private static byte[] getCrc16(byte[] arr_buff) {
        int len = arr_buff.length;

        // 预置 1 个 16 位的寄存器为十六进制FFFF, 称此寄存器为 CRC寄存器。
        int crc = 0xFFFF;
        int i, j;
        for (i = 0; i < len; i++) {
            // 把第一个 8 位二进制数据 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器
            crc = ((crc & 0xFF00) | (crc & 0x00FF) ^ (arr_buff[i] & 0xFF));
            for (j = 0; j < 8; j++) {
                // 把 CRC 寄存器的内容右移一位( 朝低位)用 0 填补最高位, 并检查右移后的移出位
                if ((crc & 0x0001) > 0) {
                    // 如果移出位为 1, CRC寄存器与多项式A001进行异或
                    crc = crc >> 1;
                    crc = crc ^ 0xA001;
                } else
                    // 如果移出位为 0,再次右移一位
                    crc = crc >> 1;
            }
        }
        return intToBytes(crc);
    }

    /**
     * 将int转换成byte数组，低位在前，高位在后
     * 改变高低位顺序只需调换数组序号
     */
    public static byte[] intToBytes(int value) {
        byte[] src = new byte[2];
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * 将字节数组转换成十六进制字符串
     */
    public static String byteTo16String(byte[] data) {
        StringBuffer buffer = new StringBuffer();
        for (byte b : data) {
            buffer.append(byteTo16String(b));
        }
        return buffer.toString();
    }

    /**
     * 将字节转换成十六进制字符串
     * <p>
     * int转byte对照表
     * [128,255],0,[1,128)
     * [-128,-1],0,[1,128)
     */
    public static String byteTo16String(byte b) {
        StringBuffer buffer = new StringBuffer();
        int aa = (int) b;
        if (aa < 0) {
            buffer.append(Integer.toString(aa + 256, 16) + "");
        } else if (aa == 0) {
            buffer.append("00");
        } else if (aa > 0 && aa <= 15) {
            buffer.append("0" + Integer.toString(aa, 16) + "");
        } else if (aa > 15) {
            buffer.append(Integer.toString(aa, 16) + "");
        }
        return buffer.toString();
    }

    public static int byteToUnsignedInt(byte[] b) {
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put(b[3]);
        buffer.put(b[2]);
        buffer.put(b[1]);
        buffer.put(b[0]);
        buffer.rewind();
        return buffer.getInt();
    }

    public static byte[] getCheckedArray(byte[] data) {
        byte bb = checkAdd(data, data.length);
        byte[] cc = new byte[data.length + 1];
        System.arraycopy(data, 0, cc, 0, data.length);
        cc[cc.length - 1] = bb;
        return cc;
    }

    public static byte checkAdd(byte[] data, int size) {
        int add = 0;
        for (int i = 0; i < size; i++) {
            add = add + data[i];
        }
        return (byte) (add & 0xFF);
    }

    //无符号16位byte转int
    public static int convertUint16ToInt(byte high, byte low) {
        return (((0x000000ff & high) << 8) & 0x0000ff00) | (0x000000ff & low);
    }

    public static int convertInt16ToInt(byte byte1, byte byte2) {
        // 将第一个字节（高位）左移8位，将第二个字节（低位）右移0位（实际上不变）
        // 注意：由于int16_t是有符号的，我们不需要对字节进行无符号处理（即不需要 & 0xFF）
        // 但是，由于Java的byte是有符号的，当高位字节为负数时，左移后需要处理符号扩展问题
        // 这里我们采用先转换为正数再左移，最后根据最高位（符号位）决定是否加上0x10000来模拟符号

        int high = byte1;
        int low = byte2;

        // 组合成一个16位的整数（此时可能是负数）
        int combined = (high << 8) | (low & 0xFF); // 低位字节不需要处理符号，但保持格式一致

        // 如果最高位是1（负数），则加上0x10000（即2^16）再取反得到正确的负数
        if ((combined & 0x8000) != 0) {
            combined = combined | 0xFFFF0000; // 设置为负数（符号扩展）
            combined = ~combined + 1; // 取反加1得到负数
        }

        // 注意：由于Java的int是32位的，而我们的结果是一个16位的值（可能扩展为32位），
        // 所以这里的结果在-32768到32767之间（对于有符号的16位整数）。
        // 如果你的应用场景需要保持为16位（例如，用于位操作或特定协议），
        // 你可能需要在后续处理中将其截断或掩码。

        // 但是，在这个例子中，我们直接返回32位的int值，因为它能够正确地表示-32768到32767之间的所有值。

        return combined;
    }


}
